#include "MapLoader.h"
#include "Base/Logger.h"
#include "TinyXml/tinyxml.h"
#include <SFML/Graphics.hpp>

#include "ScriptComponent.h"
#include "PhysicComponents.h"
#include "PositionComponent.h"
#include "GraphicComponents.h"
#include "BulletComponent.h"
#include "ParticleComponent.h"
#include "LifeComponent.h"
#include "CharacterComponent.h"

#include "BulletSystem.h"
#include "RenderSystem.h"
#include "PhysicSystem.h"
#include "BulletSystem.h"
#include "DamageSystem.h"
#include "ParticuleSystem.h"

#include "MathUtiles.h"
#include "Base/BPL.h"
#include "GameState.h"

std::string getModelFile(std::vector<TileSet>& tilesets, int id, TileSet* result)
{
	int resultFirstElem = 0;
	if(tilesets.empty())
		return "empty";

	std::map<int, std::string> mapResult = tilesets.begin()->tiles;
	std::vector<TileSet>::iterator itr;

	for(itr = tilesets.begin(); itr != tilesets.end(); itr++)
	{
		if(itr->firstElem > resultFirstElem && itr->firstElem <= id)
		{
			resultFirstElem = itr->firstElem;
			mapResult = itr->tiles;
			if(result != NULL)
			{
				result->firstElem = itr->firstElem;
				result->tiles = itr->tiles;
				result->properties = itr->properties;
			}
		}
	}
	
	return mapResult[id - resultFirstElem];
}

MapLoader::MapLoader()
{
}

MapLoader::MapLoader(const std::string& filename, BPL::EntityManager* parent, GameState* state) /*: mpt_map(load(filename, parent))*/
{
	
}

MapLoader::~MapLoader()
{
}

bool MapLoader::load(const std::string& filename, BPL::EntityManager* parent, GameState* state)
{	
	TiXmlDocument doc(filename.c_str());
	bool loadOkay = doc.LoadFile();
	if (loadOkay)
	{
		TiXmlHandle hdl(&doc);
		TiXmlElement *mapElem = hdl.FirstChildElement("map").Element();

		std::vector<TileSet> tilesets;
		TilesetLoader tilesetLoader;

		if(mapElem)
		{
			sf::Vector2i mapSize;
			if(mapElem->Attribute("width") != NULL)
				mapElem->QueryIntAttribute("width", &mapSize.x);
			if(mapElem->Attribute("height") != NULL)
				mapElem->QueryIntAttribute("height", &mapSize.y);

			// RESIZE LA GRID ET MAP
			parent->getGrid()->resize(mapSize);
			parent->getMap()->resize(mapSize);
			state->mpt_physicSystem->getMap()->resize(mapSize);

			if(mapElem->Attribute("tilewidth") != NULL)
				mapElem->QueryIntAttribute("tilewidth", &parent->sizeTile);

			TiXmlElement* elmt=hdl.FirstChild("map").FirstChild("tileset").Element();

			for(elmt; elmt; elmt=elmt->NextSiblingElement("tileset"))
			{
				int first;
				if(elmt->Attribute("firstgid") != NULL)
					elmt->QueryIntAttribute("firstgid", &first);

				TiXmlElement* propertyElem = elmt->FirstChild("properties")->FirstChild("property")->ToElement();
				std::string propertyName = propertyElem->Attribute("name");
				if(propertyName == "tiles")
				{
					TileSet tileset(first,tilesetLoader.load(propertyElem->Attribute("value")));

					TiXmlElement* elmtTileset = elmt->FirstChildElement("tile");
					for(elmtTileset; elmtTileset; elmtTileset=elmtTileset->NextSiblingElement("tile"))
					{
						int id;
						elmtTileset->QueryIntAttribute("id", &id);

						for(TiXmlElement* propertyTile = elmtTileset->FirstChild("properties")->FirstChild("property")->ToElement(); propertyTile; propertyTile=propertyTile->NextSiblingElement("property"))
						{
							tileset.properties[id].insert(std::pair<std::string,std::string>(propertyTile->Attribute("name"), propertyTile->Attribute("value")));
						}
					}
					tilesets.push_back(tileset);
				}

			}

			elmt=hdl.FirstChild("map").FirstChild("layer").FirstChild("data").FirstChild("tile").Element();
			int counter = 0;

			for(elmt; elmt; elmt=elmt->NextSiblingElement("tile"))
			{
				// TILE LOADER
				sf::Vector2i position(counter%mapSize.x, counter/mapSize.x);
				
				int gid;
				if(elmt->Attribute("gid") != NULL)
				{
					elmt->QueryIntAttribute("gid", &gid);
				}

				if(gid != 0)
				{
					BPL::EntityPtr e(new BPL::Entity(parent->getNextAvailableID()));
					parent->registerStaticEntity(e, position);
					
					ModelGraphicComponentPtr graph (new ModelGraphicComponent);
					StaticComponentPtr phys (new StaticComponent);
					PositionComponentPtr pos (new PositionComponent);
					
					int sizeTile = parent->sizeTile;
					
					pos->m_position=sf::Vector2f(position.x*sizeTile,position.y*sizeTile);

					TileSet resultTileset;
					graph->model.loadModel(getModelFile(tilesets, gid, &resultTileset));

					if(resultTileset.properties[gid-resultTileset.firstElem].find("slope") != resultTileset.properties[gid-resultTileset.firstElem].end())
					{
						if(resultTileset.properties[gid-resultTileset.firstElem]["slope"] == "left")
							phys->typeStatic = StaticComponent::SLOPELEFT;	
						else if(resultTileset.properties[gid-resultTileset.firstElem]["slope"] == "right")
							phys->typeStatic = StaticComponent::SLOPERIGHT;	

					}
					else
						phys->typeStatic = StaticComponent::FULL;	

					parent->addComponent<GraphicComponent>((BPL::EntityId)e->getID(), graph);
					parent->addComponent<PositionComponent>((BPL::EntityId)e->getID(), pos);

					if(resultTileset.properties[gid-resultTileset.firstElem].find("noPhysic") == resultTileset.properties[gid-resultTileset.firstElem].end())
					{
						parent->addComponent<PhysicComponent>((BPL::EntityId)e->getID(), phys);
						state->mpt_physicSystem->addEntity(e->getID());
					}

					/*if(resultTileset.properties[gid-resultTileset.firstElem].find("script") == resultTileset.properties[gid-resultTileset.firstElem].end())
					{
						ScriptComponentPtr script (new ScriptComponent);
						script->fromFile = true;
						script->file = resultTileset.properties[gid-resultTileset.firstElem]["script"];
						parent->addComponent<ScriptComponent>((BPL::EntityId)e->getID(), script);
						state->mpt_scriptSystem->addEntity(e->getID());
					}*/
					
					state->mpt_renderSystem->addEntity(e->getID());
					
					
				}
				counter++;
			}

			elmt=hdl.FirstChild("map").FirstChild("objectgroup").Element();

			for(elmt; elmt; elmt=elmt->NextSiblingElement("objectgroup"))
			{
				TiXmlElement* objectElem = elmt->FirstChildElement("object");
				
				for(objectElem; objectElem; objectElem=objectElem->NextSiblingElement("object"))
				{
					// OBJET LOADER
					if(objectElem->Attribute("name") != NULL)
					{
						std::string objectName = objectElem->Attribute("name");
						if(objectName == "Player")
						{
							// LE PLAYER
							int sizeTile = parent->sizeTile;
							BPL::EntityPtr e(new BPL::Entity(parent->getNextAvailableID()));

							sf::Vector2f position;
							if(objectElem->Attribute("x") != NULL)
								objectElem->QueryFloatAttribute("x", &position.x);
							if(objectElem->Attribute("y") != NULL)
								objectElem->QueryFloatAttribute("y", &position.y);

							parent->registerDynamicEntity(e, position, 0);
							
							ModelGraphicComponentPtr graph (new ModelGraphicComponent);
							JumpingComponentPtr phys (new JumpingComponent);
							PositionComponentPtr pos (new PositionComponent);
							CharacterComponentPtr charac (new CharacterComponent);
							LifeComponentPtr life (new LifeComponent);

							charac->timeBetweenShots = 0.5f;
							charac->bulletSprite = "Data/bullet.mdl";
							charac->bulletDamage = 10;
							charac->timeSinceLastShot = 0.0f;
							charac->bulletPrecision = 0.1;
							charac->bulletLeft = 30;
							
							pos->m_position=position;
							phys->sizeBox = sf::Vector2f(15,30);
							phys->jumpSpeed = 600.0f;
							phys->speed = 120;
							phys->jumpLength = 3;

							life->life = 100;
							life->killAfterZero = false;

							TiXmlElement* propertyElem =  objectElem->FirstChild("properties")->FirstChildElement("property");
							while(propertyElem)
							{
								std::string nameProperty = propertyElem->Attribute("name");
								if(nameProperty == "sprite")
								{
									graph->model.loadModel(propertyElem->Attribute("value"));
								}
								else if(nameProperty == "timeBetweenShots")
								{
									propertyElem->QueryFloatAttribute("value",& charac->timeBetweenShots);
								}
								else if(nameProperty == "bulletDamage")
								{
									propertyElem->QueryIntAttribute("value", &charac->bulletDamage);
								}
								else if(nameProperty == "bulletPrecision")
								{
									propertyElem->QueryFloatAttribute("value", &charac->bulletPrecision);
								}

								propertyElem = propertyElem->NextSiblingElement("property");
							}			

							
							parent->addComponent<GraphicComponent>((BPL::EntityId)e->getID(), graph);
							parent->addComponent<CharacterComponent>((BPL::EntityId)e->getID(), charac);
							parent->addComponent<LifeComponent>((BPL::EntityId)e->getID(), life);
							parent->addComponent<PhysicComponent>((BPL::EntityId)e->getID(), phys);
							parent->addComponent<PositionComponent>((BPL::EntityId)e->getID(), pos);

							state->mpt_renderSystem->addEntity(e->getID());
							state->mpt_physicSystem->addEntity(e->getID());
							state->mpt_playerSystem->addEntity(e->getID());
							state->mpt_cameraSystem->addEntity(e->getID());
							state->mpt_damageSystem->addEntity(e->getID());
						}
						else
						{
							if(objectElem->Attribute("type") != NULL)
							{
								std::string objetType = objectElem->Attribute("type");
								if(objetType == "scripted")
								{
									BPL::EntityPtr e(new BPL::Entity(parent->getNextAvailableID()));

									sf::Vector2f position;
									if(objectElem->Attribute("x") != NULL)
										objectElem->QueryFloatAttribute("x", &position.x);
									if(objectElem->Attribute("y") != NULL)
										objectElem->QueryFloatAttribute("y", &position.y);

									parent->registerDynamicEntity(e, position, 0);
									
									PositionComponentPtr pos (new PositionComponent);
									ScriptComponentPtr script (new ScriptComponent);
									
									pos->m_position=position;
									script->fromFile = true;
									TiXmlElement* propertyElem =  objectElem->FirstChild("properties")->FirstChildElement("property");
									while(propertyElem)
									{
										std::string nameProperty = propertyElem->Attribute("name");
										if(nameProperty == "script")
										{
											script->file = propertyElem->Attribute("value");
											break;
										}

										propertyElem = propertyElem->NextSiblingElement("property");
									}						
									
									parent->addComponent<ScriptComponent>((BPL::EntityId)e->getID(), script);
									parent->addComponent<PositionComponent>((BPL::EntityId)e->getID(), pos);

									state->mpt_scriptSystem->addEntity(e->getID());
								}
								else if(objetType == "damage")
								{
									BPL::EntityPtr e(new BPL::Entity(parent->getNextAvailableID()));

									sf::Vector2f position;
									if(objectElem->Attribute("x") != NULL)
										objectElem->QueryFloatAttribute("x", &position.x);
									if(objectElem->Attribute("y") != NULL)
										objectElem->QueryFloatAttribute("y", &position.y);

									sf::Vector2f size;
									if(objectElem->Attribute("width") != NULL)
										objectElem->QueryFloatAttribute("width", &size.x);
									if(objectElem->Attribute("height") != NULL)
										objectElem->QueryFloatAttribute("height", &size.y);

									parent->registerDynamicEntity(e, position, 0);
									
									PositionComponentPtr pos (new PositionComponent);
									DynamicComponentPtr phys (new DynamicComponent);
									BulletComponentPtr bullet (new BulletComponent);
									
									pos->m_position=position;
									phys->sizeBox = size;
									phys->collide = false;

									bullet->type = BulletComponent::ZONE;

									TiXmlElement* propertyElem =  objectElem->FirstChild("properties")->FirstChildElement("property");
									while(propertyElem)
									{
										std::string nameProperty = propertyElem->Attribute("name");
										if(nameProperty == "power")
										{
											propertyElem->QueryFloatAttribute("value", &bullet->power);
											break;
										}

										propertyElem = propertyElem->NextSiblingElement("property");
									}						
									
									parent->addComponent<BulletComponent>((BPL::EntityId)e->getID(), bullet);
									parent->addComponent<DynamicComponent>((BPL::EntityId)e->getID(), phys);
									parent->addComponent<PositionComponent>((BPL::EntityId)e->getID(), pos);

									state->mpt_damageSystem->addBullet(e->getID());

								}
								else if(objetType == "graphic")
								{
									BPL::EntityPtr e(new BPL::Entity(parent->getNextAvailableID()));

									sf::Vector2f position;
									if(objectElem->Attribute("x") != NULL)
										objectElem->QueryFloatAttribute("x", &position.x);
									if(objectElem->Attribute("y") != NULL)
										objectElem->QueryFloatAttribute("y", &position.y);

									parent->registerDynamicEntity(e, position, 0);
									
									PositionComponentPtr pos (new PositionComponent);
									ModelGraphicComponentPtr graph (new ModelGraphicComponent);
									
									pos->m_position=position;

									TiXmlElement* propertyElem =  objectElem->FirstChild("properties")->FirstChildElement("property");
									while(propertyElem)
									{
										std::string nameProperty = propertyElem->Attribute("name");
										if(nameProperty == "sprite")
										{
											graph->model.loadModel(propertyElem->Attribute("value"));
										}
										else if(nameProperty == "layer")
										{
											int layer;
											propertyElem->QueryIntAttribute("value", &layer);
											graph->layer = layer;
										}

										propertyElem = propertyElem->NextSiblingElement("property");
									}						
									
									parent->addComponent<ModelGraphicComponent>((BPL::EntityId)e->getID(), graph);
									parent->addComponent<PositionComponent>((BPL::EntityId)e->getID(), pos);
									
									state->mpt_renderSystem->addEntity(e->getID());
								}
							}
						}
					}
				}
			}
		}
	}
	else
	{
		BPL::Logger::log("Failed to load file \"%s\"\n", filename.c_str());
	}
	
	return true;
}


/*void MapLoader::saveFile(const Map& map, const std::string& filename)
{

}*/

TilesetLoader::TilesetLoader()
{
}

TilesetLoader::TilesetLoader(const std::string& filename) : mpt_tileset(load(filename))
{
	
}

TilesetLoader::~TilesetLoader()
{
}

const std::map<int,std::string>& TilesetLoader::getTileset() const
{
	return mpt_tileset;
}

std::map<int,std::string> TilesetLoader::load(const std::string& filename)
{
	std::map<int,std::string> tileset;
	
	std::ifstream f;
    f.open(filename.c_str(), std::ios::in);
	if (!f)
	{
		std::string error = "Couldn't open the tileset file : ";
		error = error + filename;
		BPL::Logger::log(error.c_str());

		return tileset;
	}

    std::string str;
    while(f >> str && str != "")
	{
		std::string path;
		f >> path;
		
		tileset.insert(std::pair<int,std::string>(boost::lexical_cast<int>(str),path));
		BPL::Logger::log(path.c_str());
	}
	f.close();

	return tileset;
}
